package com.googlecode.cswish.struts.conversion;

import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Map;

import ognl.DefaultTypeConverter;

import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class EnumerationConverter extends DefaultTypeConverter {

	private static final Log logger = LogFactory.getLog(EnumerationConverter.class);

	// convert to enum
	@Override
	public Object convertValue(Map context, Object value, Class toType) {
		if (toType.isEnum()) {
			if (value == null) {
				return null;
			}
			
			try {
				boolean isDigit = false;
				String str = null;
				if (value instanceof String[]) {
					String[] ss = (String[]) value;
					if (ss.length == 1 && NumberUtils.isDigits(ss[0])) {
						isDigit = true;
						str = ss[0];
					}
				} else if (value instanceof String) {
					isDigit = NumberUtils.isDigits((String)value);
					str = (String)value;
				}
				
				if (isDigit) {
					Method enumConstantDirectory = toType.getDeclaredMethod("values");
					Object[] result = (Object[])enumConstantDirectory.invoke(null);
					int index = Integer.valueOf(str);
					if (index >= result.length) {
						index = 0;
						logger.error("Invalid enum type:" + str + ", can't convert it to " + toType);
					}
					return result[index];
				} else if (value instanceof String[]) {
					String[] ss = (String[]) value;
				
					Object[] oo = new Object[ss.length];
					for (int i = 0; i < ss.length; i++) {
						oo[i] = Enum.valueOf(toType, ss[i]);
					}
					if (toType.isArray()) {
						return oo;
					} else {
						if (oo.length > 0) {
							return oo[0];
						} else {
							return null;
						}
					}
				} else if (value instanceof String) {
					return Enum.valueOf(toType, str);
				}
			} catch (Exception ex) {
				logger.error("Failed to convert string to enum", ex);
				return null;
			}
		}
		return super.convertValue(context, value, toType);
	}
	
	// convert to string or number
	@Override
	public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) {
		if (toType.isAssignableFrom(String.class)) {
			if (value == null) {
				return null;
			} else {
				return value.toString();
			}
		} else if (toType.isAssignableFrom(Number.class)) {
			if (value == null) {
				return null;
			} else {
				return ((Enum)value).ordinal();
			}
		}
		return super.convertValue(context, target, member, propertyName, value, toType);
	}
}